﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using DeferredRenderer.Library;
using DeferredRenderer.Library.Scene;

namespace DeferredRenderer.Editor.Gui
{
    public partial class ScenePanel : UserControl
    {
        private DeferredBase deferredBase;
        private SceneGraph scene;
        private int objectCounter = 1;
        private int terrainCounter = 1;
        private int directionalLightCounter = 1;
        private int pointLightCounter = 1;
        private int spotLightCounter = 1;
        private int ambientLightCounter = 1;
        private Dictionary<DrawableObject, TreeNode> objectToNode = new Dictionary<DrawableObject, TreeNode>();
        private Dictionary<Terrain, TreeNode> terrainToNode = new Dictionary<Terrain, TreeNode>();
        private Dictionary<DirectionalLight, TreeNode> directionalToNode = new Dictionary<DirectionalLight, TreeNode>();
        private Dictionary<PointLight, TreeNode> pointToNode = new Dictionary<PointLight, TreeNode>();
        private Dictionary<SpotLight, TreeNode> spotToNode = new Dictionary<SpotLight, TreeNode>();
        private Dictionary<AmbientLight, TreeNode> ambientToNode = new Dictionary<AmbientLight, TreeNode>();

        public ScenePanel(DeferredBase deferredBase, SceneGraph scene)
        {
            InitializeComponent();

            this.scene = scene;
            this.deferredBase = deferredBase;

            foreach (DrawableObject o in scene.DrawableObjects)
            {
                AddObject(o);
            }
            foreach (Terrain t in scene.Terrains)
            {
                AddTerrain(t);
            }
            foreach (DirectionalLight light in scene.DirectionalLights)
            {
                AddDirectionalLight(light);
            }
            foreach (PointLight light in scene.PointLights)
            {
                AddPointLight(light);
            }
            foreach (SpotLight light in scene.SpotLights)
            {
                AddSpotLight(light);
            }
            foreach (AmbientLight light in scene.AmbientLights)
            {
                AddAmbientLight(light);
            }
            scene.DrawableObjectAdded += new DrawableObjectAddedEvent(AddObject);
            scene.TerrainAdded += new TerrainAddedEvent(AddTerrain);
            scene.DirectionalLightAdded += new DirectionalLightAddedEvent(AddDirectionalLight);
            scene.PointLightAdded += new PointLightAddedEvent(AddPointLight);
            scene.SpotLightAdded += new SpotLightAddedEvent(AddSpotLight);
            scene.AmbientLightAdded += new AmbientLightAddedEvent(AddAmbientLight);
            scene.DrawableObjectDeleted += new DrawableObjectDeletedEvent(DeleteObject);
            scene.TerrainDeleted += new TerrainDeletedEvent(TerrainDeleted);
            scene.DirectionalLightDeleted += new DirectionalLightDeletedEvent(DeleteDirectionalLight);
            scene.PointLightDeleted += new PointLightDeletedEvent(DeletePointLight);
            scene.SpotLightDeleted += new SpotLightDeletedEvent(DeleteSpotLight);
            scene.AmbientLightDeleted += new AmbientLightDeletedEvent(DeleteAmbientLight);
        }

        private void AddAmbientLight(AmbientLight light)
        {
            TreeNode newNode = new TreeNode("AmbientLight" + ambientLightCounter);
            newNode.Tag = light;
            newNode.ContextMenuStrip = ambientLightMenuStrip;
            ambientLightCounter++;
            sceneTree.Nodes["lightRoot"].Nodes["ambientRoot"].Nodes.Add(newNode);
            ambientToNode.Add(light, newNode);
            sceneTree.SelectedNode = newNode;
        }

        private void AddSpotLight(SpotLight light)
        {
            TreeNode newNode = new TreeNode("SpotLight" + spotLightCounter);
            newNode.Tag = light;
            newNode.ContextMenuStrip = spotLightMenuStrip;
            spotLightCounter++;
            sceneTree.Nodes["lightRoot"].Nodes["spotRoot"].Nodes.Add(newNode);
            spotToNode.Add(light, newNode);
            sceneTree.SelectedNode = newNode;
        }

        private void AddPointLight(PointLight light)
        {
            TreeNode newNode = new TreeNode("PointLight" + pointLightCounter);
            newNode.Tag = light;
            newNode.ContextMenuStrip = pointLightMenuStrip;
            pointLightCounter++;
            sceneTree.Nodes["lightRoot"].Nodes["pointRoot"].Nodes.Add(newNode);
            pointToNode.Add(light, newNode);
            sceneTree.SelectedNode = newNode;
        }

        private void AddDirectionalLight(DirectionalLight light)
        {
            TreeNode newNode = new TreeNode("DirectionalLight" + directionalLightCounter);
            newNode.Tag = light;
            newNode.ContextMenuStrip = directionalLightMenuStrip;
            directionalLightCounter++;
            sceneTree.Nodes["lightRoot"].Nodes["directionalRoot"].Nodes.Add(newNode);
            directionalToNode.Add(light, newNode);
            sceneTree.SelectedNode = newNode;
        }

        private void AddObject(DrawableObject o)
        {
            TreeNode newNode = new TreeNode("DrawableObject" + objectCounter);
            newNode.Tag = o;
            newNode.ContextMenuStrip = objectMenuStrip;
            objectCounter++;
            sceneTree.Nodes["objectRoot"].Nodes.Add(newNode);
            objectToNode.Add(o, newNode);
            sceneTree.SelectedNode = newNode;
        }

        private void AddTerrain(Terrain t)
        {
            TreeNode newNode = new TreeNode("Terrain" + terrainCounter);
            newNode.Tag = t;
            newNode.ContextMenuStrip = terrainMenuStrip;
            terrainCounter++;
            sceneTree.Nodes["terrainRoot"].Nodes.Add(newNode);
            terrainToNode.Add(t, newNode);
            sceneTree.SelectedNode = newNode;
        }

        private void DeleteAmbientLight(AmbientLight light)
        {
            sceneTree.Nodes["lightRoot"].Nodes["ambientRoot"].Nodes.Remove(ambientToNode[light]);
        }

        private void DeleteSpotLight(SpotLight light)
        {
            sceneTree.Nodes["lightRoot"].Nodes["spotRoot"].Nodes.Remove(spotToNode[light]);
        }

        private void DeletePointLight(PointLight light)
        {
            sceneTree.Nodes["lightRoot"].Nodes["pointRoot"].Nodes.Remove(pointToNode[light]);
        }

        private void DeleteDirectionalLight(DirectionalLight light)
        {
            sceneTree.Nodes["lightRoot"].Nodes["directionalRoot"].Nodes.Remove(directionalToNode[light]);
        }

        private void DeleteObject(DrawableObject o)
        {
            sceneTree.Nodes["objectRoot"].Nodes.Remove(objectToNode[o]);
        }

        void TerrainDeleted(Terrain t)
        {
            sceneTree.Nodes["terrainRoot"].Nodes.Remove(terrainToNode[t]);
        }

        private void addObjectMenuItem_Click(object sender, EventArgs e)
        {
            scene.Add(new DrawableObject());
        }

        private void addTerrainMenuItem_Click(object sender, EventArgs e)
        {
            scene.Add(new Terrain());
        }

        private void addAmbientLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Add(new AmbientLight());
        }

        private void addPointLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Add(new PointLight());
        }

        private void addSpotLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Add(new SpotLight());
        }

        private void addDirectionalLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Add(new DirectionalLight());
        }

        private void deleteAmbientLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Delete((AmbientLight)sceneTree.SelectedNode.Tag);
        }

        private void deleteObjectMenuItem_Click(object sender, EventArgs e)
        {
            scene.Delete((DrawableObject)sceneTree.SelectedNode.Tag);
        }

        private void deleteTerrainMenuItem_Click(object sender, EventArgs e)
        {
            scene.Delete((Terrain)sceneTree.SelectedNode.Tag);
        }

        private void deletePointLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Delete((PointLight)sceneTree.SelectedNode.Tag);
        }

        private void deleteSpotLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Delete((SpotLight)sceneTree.SelectedNode.Tag);
        }

        private void deleteDirectionalLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Delete((DirectionalLight)sceneTree.SelectedNode.Tag);
        }

        private void sceneTree_MouseClick(object sender, MouseEventArgs e)
        {
            if (MouseButtons.Right == e.Button)
            {
                sceneTree.SelectedNode = sceneTree.GetNodeAt(e.X, e.Y);
            }
        }

        private void sceneTree_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (sceneTree.SelectedNode.Parent != null)
            {
                switch (sceneTree.SelectedNode.Parent.Name)
                {
                    case "ambientRoot":
                        AmbientLight ambientLight = (AmbientLight)sceneTree.SelectedNode.Tag;
                        if (editPanel.Controls.Count > 0) editPanel.Controls.Clear();
                        AmbientLightPanel ambientPanel = new AmbientLightPanel(ambientLight);
                        ambientPanel.Dock = DockStyle.Fill;
                        editPanel.Controls.Add(ambientPanel);
                        break;
                    case "directionalRoot":
                        DirectionalLight directionalLight = (DirectionalLight)sceneTree.SelectedNode.Tag;
                        if (editPanel.Controls.Count > 0) editPanel.Controls.Clear();
                        DirectionalLightPanel directionalPanel = new DirectionalLightPanel(directionalLight);
                        directionalPanel.Dock = DockStyle.Fill;
                        editPanel.Controls.Add(directionalPanel);
                        break;
                    case "pointRoot":
                        PointLight pointLight = (PointLight)sceneTree.SelectedNode.Tag;
                        if (editPanel.Controls.Count > 0) editPanel.Controls.Clear();
                        PointLightPanel pointPanel = new PointLightPanel(pointLight);
                        pointPanel.Dock = DockStyle.Fill;
                        editPanel.Controls.Add(pointPanel);
                        break;
                    case "spotRoot":
                        SpotLight spotLight = (SpotLight)sceneTree.SelectedNode.Tag;
                        if (editPanel.Controls.Count > 0) editPanel.Controls.Clear();
                        SpotLightPanel spotPanel = new SpotLightPanel(spotLight);
                        spotPanel.Dock = DockStyle.Fill;
                        editPanel.Controls.Add(spotPanel);
                        break;
                    case "objectRoot":
                        DrawableObject o = (DrawableObject)sceneTree.SelectedNode.Tag;
                        if (editPanel.Controls.Count > 0) editPanel.Controls.Clear();
                        DrawableObjectPanel objectPanel = new DrawableObjectPanel(o);
                        objectPanel.Dock = DockStyle.Fill;
                        editPanel.Controls.Add(objectPanel);
                        break;
                    case "terrainRoot":
                        Terrain t = (Terrain)sceneTree.SelectedNode.Tag;
                        if (editPanel.Controls.Count > 0) editPanel.Controls.Clear();
                        TerrainPanel terrainPanel = new TerrainPanel(t);
                        terrainPanel.Dock = DockStyle.Fill;
                        editPanel.Controls.Add(terrainPanel);
                        break;
                    default:
                        editPanel.Controls.Clear();
                        break;
                }
            }
        }

        private void sceneTree_KeyUp(object sender, KeyEventArgs e)
        {
            if (Keys.Delete == e.KeyCode || Keys.Back == e.KeyCode && null != sceneTree.SelectedNode.Parent)
            {
                switch (sceneTree.SelectedNode.Parent.Name)
                {
                    case "ambientRoot":
                        scene.Delete((AmbientLight)sceneTree.SelectedNode.Tag);
                        break;
                    case "directionalRoot":
                        scene.Delete((DirectionalLight)sceneTree.SelectedNode.Tag);
                        break;
                    case "pointRoot":
                        scene.Delete((PointLight)sceneTree.SelectedNode.Tag);
                        break;
                    case "spotRoot":
                        scene.Delete((SpotLight)sceneTree.SelectedNode.Tag);
                        break;
                    case "objectRoot":
                        scene.Delete((DrawableObject)sceneTree.SelectedNode.Tag);
                        break;
                    case "terrainRoot":
                        scene.Delete((Terrain)sceneTree.SelectedNode.Tag);
                        break;
                }
            }
        }
    }
}
